home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / scope / 001-025 / scopedisk9 / sound / sound.c < prev    next >
C/C++ Source or Header  |  1995-03-18  |  17KB  |  543 lines

  1. /****************************   Sound.c   *********************************
  2.  
  3.     Sound is copyright (c) 1988 by Richard Lee Stockton, 21305 60th Ave W.,
  4. Mountlake Terrace, Washington 98043, 206/776-1253(voice), but may be freely
  5. distributed as long as no profit is made from its distribution or sale
  6. without my written permission. I call this concept 'FreeWare', you can
  7. call it whatcha want.
  8.  
  9.     I also include the source code, (Manx 3.4/3.6), in the hope that it
  10. will be of benefit to someone in the Amiga programming community. Feel free
  11. to alter it at will, (but at your own risk!). I _would_ appreciate
  12. receiving a copy of whatever it may become and it is always nice to receive
  13. credit, (perhaps just a few lines of glowing tribute.^)
  14.  
  15.                Long Live Leo and All the Little Schwabies!
  16.  
  17.                     To Manufacture with Manx 3.4/3.6:
  18.  
  19.                             cc Sound.c
  20.                             ln +cd Sound.o -lc
  21.  
  22.      You'll get a warning that you've re-defined IconBase. Ignore it.
  23.  
  24. **************************************************************************/
  25.  
  26. #include <exec/memory.h>
  27. #include <workbench/startup.h>
  28. #include <workbench/workbench.h>
  29. #include <workbench/icon.h>
  30. #include <libraries/dosextens.h>
  31. #include <devices/audio.h>
  32. #include <functions.h>
  33.  
  34. /* Less than WorkBench 1.2 need not apply. That's nobody, right? ;-> */
  35.  
  36. #define  REVISION   33L
  37.  
  38. /* We'll need 4 buffers in CHIP memory, all else in FAST, if ya got it */
  39.  
  40. #define  BUFSIZE  1024L
  41.  
  42. /* Some MANX specific, byte saving, (usually), string routines */
  43.  
  44. #define  strcpy     _BUILTIN_strcpy
  45. #define  strcmp     _BUILTIN_strcmp
  46. #define  strlen     _BUILTIN_strlen
  47.  
  48. /* A pretty little HELP message showing valid variable ranges */
  49.  
  50. #define  USAGE      "\
  51. \033[42m\033[31m\
  52.  USAGE: Sound <file> [0-55]     [56-65535]    [on/off] \
  53. \033[m\n\033[42m\
  54.  ABORT: <CTRL> 'c'\033[33m\
  55.    CYCLES SAMPLES_PER_SECOND STEREO  \
  56. \033[m\n\033[42m\033[31m\
  57.  05/18/88 04:28  copyright \xa9 1988 Richard Lee Stockton \
  58. \033[m\n"
  59.  
  60. /* Probably more GLOBALS than are required. 'C' is funny stuff. */
  61.  
  62. extern struct   IconBase      *IconBase=NULL;
  63. extern struct   IntuitionBase *IntuitionBase=NULL;
  64. extern struct   WBStartup     *WBenchMsg=NULL;
  65. extern struct   WBArg         *argp=NULL;
  66.        struct   IntuiMessage  *message=NULL;
  67.        struct   Window        *StatusWindo=NULL;
  68.        struct   DiskObject    *infofile=NULL;
  69.        struct   IOAudio       *sound[4]={NULL,NULL,NULL,NULL};
  70.                 long          sactual=0L, sstart=0L, savelock=0L,
  71.                                atol(), sps=0L, cycles=1L, lock=0L;
  72.                 short         k=0;
  73.                 UBYTE         sunit[4]={12,10,5,3};
  74.                 BOOL          stereo=FALSE, help=FALSE;
  75.                 char          *sbuffer=NULL, *ltoa(),
  76.                                title[50]="Sound: ",
  77.                                *cbuf[4]={NULL,NULL,NULL,NULL},
  78.                                *sname[108]=NULL, *SafeAllocMem();
  79.                 void          loadSound(), setStatusWindo(),
  80.                                soundSound(), quit();
  81.  
  82. /*********** quit, give-up, go home, finish... Neatness counts! ******/
  83.  
  84. void quit(string)
  85. char    *string;
  86. {
  87.    if(sound[0])      /* This cleans up the audio device stuff */
  88.    {
  89.       for(k=3;k>(-1);k--)    if(sound[k]) AbortIO(sound[k]);
  90.       if(sound[0]->ioa_Request.io_Device) CloseDevice(sound[0]);
  91.       for(k=3;k>(-1);k--)
  92.       {
  93.          if(sound[k]->ioa_Request.io_Message.mn_ReplyPort)
  94.             DeletePort(sound[k]->ioa_Request.io_Message.mn_ReplyPort);
  95.       }
  96.       for(k=3;k>(-1);k--)
  97.       {
  98.          if(sound[k]) FreeMem(sound[k],(long)sizeof(struct IOAudio));
  99.          if(cbuf[k])  FreeMem(cbuf[k],BUFSIZE);
  100.       }
  101.       sound[0]=NULL;
  102.    }
  103.  
  104. /* Write any message to out. May be error or could be samples/second */
  105. /* You'll be sorry if you try to Write(Output()) to WorkBench!  8-)  */
  106.  
  107.    if(help) Write(Output(),string,(long)strlen(string));
  108.    else if(*string)
  109.    {
  110.       strcpy(title,"Sound Error: ");   strcat(title,string);
  111.       setStatusWindo();
  112.    }
  113.    if(!help&&(cycles>-1L)&&(sactual<(16000L*(stereo+1L)))) Delay(77L);
  114.  
  115. /* Clean up everything else */
  116.  
  117.    if(StatusWindo)      CloseWindow(StatusWindo);
  118.    if(IntuitionBase)    CloseLibrary(IntuitionBase);
  119.    if(sbuffer)          FreeMem(sbuffer,sactual);
  120.    if(infofile)         FreeDiskObject(infofile);
  121.    if(IconBase)         CloseLibrary(IconBase);
  122.    if(sound[3]) exit(0); else exit(10);
  123. }
  124.  
  125.  
  126. /*  Don't Allocate if Low Mem - by Bryce Nesbitt  */
  127. /* Aberations by RLS. 4096 should be fudge enough */
  128.  
  129. char *SafeAllocMem(size,flags)
  130. long size, flags;
  131. {
  132.    register char *p;
  133.    
  134.    if(p=(char *)AllocMem(size,flags))
  135.       if(AvailMem(MEMF_CHIP)<4096L)
  136.          { FreeMem(p,size);  return(NULL); }
  137.    return(p);
  138. }
  139.  
  140. /**** Status Window. Uses <CTRL> 'c' to abort.  ****/
  141.  
  142. void setStatusWindo()
  143. {
  144.     struct NewWindow w;
  145.     
  146. /* Try to Open Intuition and if successful, make a 'status' window */
  147.  
  148.     if(StatusWindo) CloseWindow(StatusWindo); StatusWindo=NULL;
  149.     if(!IntuitionBase)
  150.     {
  151.        IntuitionBase = (struct IntuitionBase *)
  152.                        OpenLibrary("intuition.library",REVISION);
  153.     }
  154.     if(!IntuitionBase) quit(NULL);
  155.  
  156.     w.LeftEdge    =   82L;
  157.     w.TopEdge     =    0L;
  158.  
  159. /* if title==USAGE, the large Width value will abort the window open */
  160. /* This is just fine, because we don't WANT it to open in this case. */
  161.  
  162.     w.Width       = (long)(strlen(title)*8+8);
  163.     w.Height      =   10L;
  164.     w.DetailPen   =  0x01;
  165.     w.BlockPen    =  0x03;
  166.     w.Title       = (UBYTE *)title;
  167.     w.Flags       = SMART_REFRESH|NOCAREREFRESH|ACTIVATE;
  168.     w.IDCMPFlags  = VANILLAKEY;   /* So we can get <CTRL> 'c' message */
  169.     w.Type        = WBENCHSCREEN;
  170.     w.FirstGadget = NULL;
  171.     w.CheckMark   = NULL;
  172.     w.Screen      = NULL;
  173.     w.BitMap      = NULL;
  174.     w.MinWidth    = 0;
  175.     w.MinHeight   = 0;
  176.     w.MaxWidth    = 0;
  177.     w.MaxHeight   = 0;
  178.     StatusWindo = OpenWindow(&w);
  179.  
  180. /* Abort ONLY if cycles = 0 (loop) AND the window failed to open. */
  181. /* This set of conditions would otherwise result in an Endless and */
  182. /* Escape-proof loop. If cycles != 0, sound will end, sometime... */
  183.  
  184.     if(!StatusWindo&&(cycles==0L)) quit(NULL);
  185. }
  186.  
  187.  
  188. /******** Load SoundFile 'sPath' & set cycles-sps-stereo *******/
  189.  
  190. void loadSound(sPath)
  191. char    *sPath[80];
  192. {
  193.    struct FileHandle    *sFile=NULL;
  194.    struct FileInfoBlock *finfo=NULL;
  195.    struct FileLock      *lock=NULL;
  196.           long          i, j;
  197.           char          string[5];
  198.  
  199. /* Allocate 256 bytes as work memory */
  200.  
  201.    if(!(sbuffer=SafeAllocMem(256L,MEMF_CLEAR|MEMF_PUBLIC)))
  202.         quit("No Work Memory!");
  203.  
  204. /* Check for and parse IFF data in first 256 bytes of file */
  205.  
  206.    if(!(sFile=Open(sPath,MODE_OLDFILE)))
  207.    {
  208.       sactual=256L;      quit("Can't Find SoundFile!");
  209.    }
  210.    Read(sFile,sbuffer,256L);              /* load the 1st 256 bytes */
  211.    for(sstart=0L, sps=0L, i=0L; i<252L; i+=4L)
  212.    {
  213.       strncpy(string,sbuffer+i,4);   string[4]=NULL;
  214.       if(!(strcmp(string,"VHDR")))        /* get samples per second */
  215.       {
  216.          for(j=0;j<(long)((UBYTE)sbuffer[i+20]);j++) sps+=256L;
  217.          sps += ((UBYTE)sbuffer[i+21L]);
  218.       }
  219.       if(!(strcmp(string,"CHAN")))            /* Channel Assignment */
  220.       {
  221.          if((sbuffer[i+7]==6)||(sbuffer[i+11]==6)) stereo=TRUE;
  222.       }
  223.       if(!(strcmp(string,"BODY")))        /* get size of sound data */
  224.       {
  225.          for(j=0;j<4;j++) sactual+=(((UBYTE)sbuffer[i+7L-j])<<(8*j));
  226.          sstart = i+8L; i=252L;
  227.       }
  228.    }
  229.  
  230. /* if not in IFF format, get filesize from FileInfoBlock */
  231.  
  232.    if(!sactual)
  233.    {
  234.  
  235. /* Allocate a file info block, get size from it, and de-allocate */
  236.  
  237.       if((!(finfo=(struct FileInfoBlock *)
  238.          SafeAllocMem((long)sizeof(struct FileInfoBlock),MEMF_CLEAR)))
  239.        ||(!(lock=Lock(sname,ACCESS_READ)))||(!(Examine(lock,finfo))) )
  240.                     quit("FileInfoBlock Problem!");
  241.       sactual = finfo->fib_Size;      if(lock) UnLock(lock);
  242.       if(finfo) FreeMem(finfo,(long)sizeof(struct FileInfoBlock));
  243.    }
  244.  
  245. /* clean up work area */
  246.  
  247.    FreeMem(sbuffer,256L); sbuffer=NULL;
  248.  
  249. /* Allocate _contiguous_ memory for SOUND data. */
  250. /* We'll transfer in BUFSIZE chunks to CHIP memory a little later. */
  251. /* We have to do the contiguity(?) check since AllocMem() does not. */
  252.  
  253.    if((AvailMem(MEMF_LARGEST)<sactual) ||
  254.      (!(sbuffer=SafeAllocMem(sactual,MEMF_CLEAR|MEMF_PUBLIC))))
  255.         { Close(sFile); quit("Need Contiguous Memory!"); }
  256.  
  257. /* Load the data into sbuffer */
  258.  
  259.    Seek(sFile,sstart,OFFSET_BEGINNING);
  260.    if((Read(sFile,sbuffer,sactual)) == -1L)
  261.       { Close(sFile); quit("Read Error!");}
  262.    Close(sFile);
  263. }
  264.  
  265. /*****************  make a noise ******************/
  266.  
  267. void soundSound()
  268. {
  269.     ULONG   class;
  270.     LONG    i, dactual, dlength, remaining;
  271.     USHORT  code, count;
  272.  
  273. /* Put up a 'status' window on the top line. */
  274.  
  275.     strcat(title,ltoa(sps)); strcat(title," SAMPLES_PER_SECOND");
  276.     if(stereo) strcat(title," in STEREO");     setStatusWindo();
  277.  
  278. /* Allocate sound data buffers from CHIP memory. Ports and */
  279. /* Audio Request Structures do NOT require CHIP memory */
  280.  
  281.    for(k=0;k<4;k++)
  282.    {
  283.      if(!(cbuf[k]=SafeAllocMem(BUFSIZE,
  284.            MEMF_CHIP|MEMF_CLEAR|MEMF_PUBLIC))) quit("No CHIP Memory!");
  285.      if(!(sound[k]=(struct IOAudio *)SafeAllocMem((long)sizeof(struct IOAudio),
  286.                      MEMF_CLEAR|MEMF_PUBLIC))) quit("No IOA Memory!");
  287.    }
  288.    if( (!(sound[0]->ioa_Request.io_Message.mn_ReplyPort =
  289.            CreatePort("Sound0",0L))) ||
  290.        (!(sound[1]->ioa_Request.io_Message.mn_ReplyPort =
  291.            CreatePort("Sound1",0L))) ||
  292.        (!(sound[2]->ioa_Request.io_Message.mn_ReplyPort =
  293.            CreatePort("Sound2",0L))) ||
  294.        (!(sound[3]->ioa_Request.io_Message.mn_ReplyPort =
  295.            CreatePort("Sound3",0L))) )         quit("No Port Memory!");
  296.    
  297.  
  298. /* Open Audio using the first IOAudio as the 'initializer' request */
  299.  
  300.    sound[0]->ioa_Request.io_Message.mn_Node.ln_Pri = 20;
  301.    sound[0]->ioa_Data   = &sunit[0];
  302.    sound[0]->ioa_Length = 4L;
  303.    if((OpenDevice(AUDIONAME,0L,sound[0],0L))!=NULL)
  304.       quit("No Audio Device!");
  305.  
  306. /* Set all IOAudios. */
  307.  
  308.    for(k=0;k<4;k++)
  309.    {
  310.       sound[k]->ioa_Request.io_Message.mn_Node.ln_Pri = 20;
  311.       sound[k]->ioa_Request.io_Command = CMD_WRITE;
  312.       sound[k]->ioa_Request.io_Flags   = ADIOF_PERVOL;
  313.  
  314. /* Note copies of Device & AllocKey from initializer. */
  315.  
  316.       sound[k]->ioa_Request.io_Device  = sound[0]->ioa_Request.io_Device;
  317.       sound[k]->ioa_AllocKey  = sound[0]->ioa_AllocKey;
  318.  
  319. /* Each IOAudio has its own CHIP buffer, Port, and Unit (left/right) */
  320.  
  321.       sound[k]->ioa_Data   = (UBYTE *)cbuf[k];
  322.  
  323. /* 3579547 divided by 55 = 65083, nearly the maximum Period (65535) */
  324.  
  325.       sound[k]->ioa_Period = 3579547L/sps;
  326.  
  327. /* As LOUD as possible. Use your monitor/stereo volume. Rock 'n Roll! */
  328.  
  329.       sound[k]->ioa_Volume = 64L;
  330.  
  331. /* One time through this BUFSIZE (or smaller) part of the whole */
  332.  
  333.       sound[k]->ioa_Cycles = 1L;
  334.    }
  335.  
  336. /* The compiler wants 'Unit' to be a structure, we just want to mask */
  337. /* into the allocated left/right channels. left=1 or 8, right=2 or 4 */
  338. /*        ...zap! You're a Unit structure! Feel any different?       */
  339.  
  340.    for(k=2;k>(-1);k-=2)
  341.    {
  342.       sound[k+1]->ioa_Request.io_Unit = (struct Unit *)
  343.                       ((ULONG)(sound[0]->ioa_Request.io_Unit)&6L);
  344.       sound[k]->ioa_Request.io_Unit  = (struct Unit *)
  345.                       ((ULONG)(sound[0]->ioa_Request.io_Unit)&9L);
  346.    }
  347.  
  348. /* If in STEREO, split file. If in MONO, 'b' buffers use 'a' data */
  349.  
  350.    if(stereo) remaining=(sactual/2L)-(sactual&1L);
  351.    else
  352.    {
  353.       remaining=sactual;
  354.       sound[1]->ioa_Data   = (UBYTE *)cbuf[0];
  355.       sound[3]->ioa_Data   = (UBYTE *)cbuf[2];
  356.    }
  357.  
  358. /* dactual is the length of one channel's complete data */
  359.  
  360.    dactual=remaining;     k=count=0;
  361.  
  362. /* we be doing loops here */
  363.  
  364.    do
  365.    {
  366.  
  367. /* be CERTAIN ioa_Length is an even number & set datalength */
  368.  
  369.       if(remaining>BUFSIZE) dlength=BUFSIZE;
  370.        else  { dlength=remaining;  dlength-=(dlength&1L); }
  371.  
  372. /* Move the data into the proper CHIP buffer of BUFSIZE */
  373.  
  374.       movmem(sbuffer+(dactual-remaining),cbuf[k],(int)dlength);
  375.  
  376. /* Don't load or use the right CHIP buffers if MONO. Saves time. */
  377.  
  378.       if(stereo) movmem(sbuffer+(sactual-remaining),
  379.                         cbuf[k+1],(int)dlength);
  380.  
  381. /* Data has been moved, so adjust 'remaining' */
  382.  
  383.       remaining-=dlength;
  384.  
  385. /* Left and Right Lengths are the same, no matter what! */
  386.  
  387.       sound[k]->ioa_Length = sound[k+1]->ioa_Length = dlength;
  388.  
  389. /* Start one set of Left/Right Channels. */
  390.  
  391.       BeginIO(sound[k]);      BeginIO(sound[k+1]);
  392.  
  393. /* Check Intuition for the ABORT message */
  394.  
  395.       while(message=(struct IntuiMessage *)GetMsg(StatusWindo->UserPort))
  396.       {
  397.          class = message->Class;  code = message->Code;
  398.  
  399. /* Hi Intuition! Thanks for the message, Have a nice day! */
  400.  
  401.          ReplyMsg(message);
  402.  
  403. /* <CTRL> 'c' abort.  1,2,3, easy as a,b,c, baby you and me! */
  404.  
  405.          if((class==VANILLAKEY)&&(code==3)) quit(NULL);
  406.       }
  407.  
  408. /* Is this the last time AND the last cycle? If yes & no, reset. */
  409.  
  410.       if(remaining<2L) if(--cycles!=0L)
  411.          { remaining=dactual;   dlength=BUFSIZE; }
  412.  
  413. /* Is this the last time, or what? */
  414.  
  415.       if(remaining<2L)  WaitIO(sound[k+1]);   /* wait for LAST request */
  416.       else
  417.       {
  418.          if(k) k=0; else k=2;    /* switch buffers & wait for PREVIOUS */
  419.          if(count++)  WaitIO(sound[k+1]);
  420.       }
  421.  
  422. /* Keep going until we run out of data */
  423.  
  424.    } while(remaining>1L);                   /* End of Loop */
  425. }
  426.  
  427.  
  428. /********** long int to char string (up to 7 digits) ***********/
  429.  
  430. char *ltoa(num)
  431.           long  num;
  432. {
  433.    static char ostring[8]="";
  434.           short  next = 7,  shift = 0;
  435.    
  436.    if (!num) ostring[next--] = '0';
  437.    while ((num+9L)/10)
  438.       { ostring[next--] = num % 10L + '0';   num /= 10L; }
  439.    next+=1;
  440.    while(next<8)       ostring[shift++] = ostring[next++];
  441.    while(shift<8)      ostring[shift++] = '\0';
  442.    return(ostring);
  443. }
  444.  
  445.  
  446. /************************  MAIN  ****************************/
  447.  
  448. main(argc,argv)
  449.  int           argc;
  450.  char          *argv[];
  451. {
  452.     long      temp=0L;
  453.     short     i=0;
  454.     char      string[10];
  455.     
  456.     if((argc==0)&&(WBenchMsg->sm_NumArgs>1))     /*  from WorkBench  */
  457.     {
  458.        argp=(WBenchMsg->sm_ArgList)+1;           /*  CD to lock and  */
  459.        if(lock=argp->wa_Lock)  savelock=(long)CurrentDir(lock);
  460.        strcpy(sname,argp->wa_Name);              /* get the filename */
  461.     }
  462.  
  463. /* If Called From CLI */
  464.  
  465.     if(argc==1) {help=TRUE; quit(USAGE);}    /* No SOUNDfile so quit */
  466.     if(argc>1)  strcpy(sname,argv[1]);   /* 2nd arg MUST be filename */
  467.  
  468. /* Load 'em up! */
  469.  
  470.     loadSound(sname);
  471.  
  472. /* If WorkBench, try to get info from the .info file */
  473.  
  474.     if((argc==0)&&(WBenchMsg->sm_NumArgs>1))
  475.     {
  476.        IconBase=(struct IconBase *)OpenLibrary("icon.library",REVISION);
  477.        if((!IconBase) ||   
  478.          ((infofile=(struct DiskObject *)GetDiskObject(sname))==NULL))
  479.                 quit(NULL);
  480.  
  481. /* then check 'ToolTypes' to set cycles, stereo, & samples per second. */
  482. /* Note use of strncpy to copy only what we need. This is an attempt to */
  483. /* take care of a .info editing bug which may cause extra long strings. */
  484.  
  485.        strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
  486.                        "CYCLES"),3);
  487.  
  488. /* We do this check for '0' since atol() thinks ' ', or NULL = '0'  */
  489. /* But if data is blank, or NULL, we want cycles set to 1 (default) */
  490.  
  491.        if(string[0]=='0') cycles = 0L;
  492.        temp=atol(string);  if(temp) cycles = temp;
  493.  
  494. /* Valid STEREO strings will have o or O as the first letter. */
  495.  
  496.        strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
  497.                        "STEREO"),4);
  498.        if(string[0]=='o'||string[0]=='O')
  499.        {
  500.           if(string[1]=='n'||string[1]=='N')   stereo = TRUE;
  501.            else  stereo = FALSE;
  502.        }
  503.  
  504. /* We'll take any old sps because we check for validity before playing */
  505.  
  506.        strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
  507.                        "SAMPLES_PER_SECOND"),6);
  508.        temp = atol(string);    if(temp) sps = temp;
  509.     } 
  510.  
  511. /* Parse arguments from CLI. Note that from WorkBench, argc will be 0, */
  512. /* so the following for() loop will only execute if called from CLI. */
  513.  
  514.     for(i=2;i<argc;i++)
  515.     {
  516.        if(argv[i][0]=='o'||argv[i][0]=='O')
  517.        {
  518.           if(argv[i][1]=='n'||argv[i][1]=='N')   stereo = TRUE;
  519.           else   stereo = FALSE;
  520.        }
  521.        else  /* must be sps or cycles, size of arg determines which one */
  522.        {
  523.           temp = atol(argv[i]);
  524.           if(temp<56L)  cycles = temp;    else  sps = temp;
  525.        }
  526.     }
  527.  
  528. /* Make sure we have valid values before 'sounding' */
  529.  
  530.     if((sps<56L)||(sps>65534L)) sps = 10000L;
  531.     if(cycles>65535L) cycles = 1L;       /* 55 limit only applies to CLI */
  532.  
  533.     soundSound();                                    /* MAKE THAT NOISE! */
  534.  
  535.     if(lock) CurrentDir(savelock);                /* restore current DIR */
  536.  
  537. /* Then release everything you've used here. (Be a tidy camper.) */
  538.  
  539.     quit(NULL);
  540. }
  541.  
  542. /************************** end of Sound.c ******************************/
  543.